﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

namespace DataProcess.CoreNew.Tools
{
    public delegate void GroupDataChanged(Dictionary<int, object> sender);

    public class OPCClient
    {
        private OPCServer _server;
        private object serverlock = new object();
        public OPCServer Server
        {
            get
            {
                if (_server == null)
                {
                    if (Monitor.TryEnter(serverlock, 2000))
                    {
                        try
                        {
                            if (_server == null)
                            {
                                _server = new OPCServer();
                            }
                        }
                        finally
                        {
                            Monitor.Exit(serverlock);
                        }
                    }
                }
                return _server;
            }
        }

        public OPCGroups OpcGroups { get; set; }

        public OPCGroup OpcGroup { get; set; }

        public OPCItems OpcItems { get; set; }

        public OPCItem OpcItem { get; set; }

        public OPCGroup Group
        {
            get { return OpcGroup; }
        }

        public string IP = string.Empty;
        public string Name { get; set; }

        public bool IsConnected
        {
            get
            {
                try
                {
                    return Server?.ServerState == 1;
                }
                catch
                {
                    return false;
                }

            }
        }

        public bool? GroupActive { get { return Group?.IsActive; } }

        public event GroupDataChanged DataChanged;

        public bool IsItemInit { get; private set; } = false;

        public List<string> GetIP()
        {
            List<string> strLst = new List<string>();
            //本机名
            string hostName = Dns.GetHostName();
            //会返回所有地址，包括IPv4和IPv6
            IPAddress[] addressList = Dns.GetHostAddresses(hostName);
            foreach (IPAddress ip in addressList)
            {
                strLst.Add(ip.ToString());
            }
            return strLst;
        }

        //获取本机OPC服务器列表
        public object GetOPCServer(out string error, string hostname = "")
        {
            error = string.Empty;
            object serverList = null;
            if (string.IsNullOrEmpty(hostname)) hostname = Dns.GetHostName();
            try
            {
                serverList = Server.GetOPCServers(hostname);
            }
            catch (Exception err)
            {
                error = err.Message;
            }
            return serverList;
        }

        /// <summary>
        /// 连接到指定的OPC服务器
        /// </summary>
        /// <param name="HostName">OPC服务器地址</param>
        /// <param name="OPCServerName">OPC服务名称</param>
        public bool ConnectRemoteServer(string OPCServerName, out string msg, string HostName = "")
        {
            if (IsConnected)
            {
                msg = $"OPC服务器【{Name}】已连接";
                return true;
            }
            try
            {
                if (string.IsNullOrEmpty(HostName))
                {
                    HostName = Dns.GetHostName();
                }
                Server.Connect(OPCServerName, HostName);
                Name = OPCServerName;
                OpcGroups = Server.OPCGroups;
                OpcGroups.DefaultGroupIsActive = true;
                OpcGroups.DefaultGroupDeadband = 0;
                msg = $"连接OPC服务器【{Name}】成功";
                return true;
            }
            catch (Exception err)
            {
                msg = $"连接时出现错误：IP:{HostName},OPCServerName:{OPCServerName}，{err.Message}";
                return false;
            }
        }

        /// <summary>
        /// 所有array的索引从1开始
        /// </summary>
        /// <param name="TransactionID"></param>
        /// <param name="NumItems"></param>
        /// <param name="ClientHandles"></param>
        /// <param name="ItemValues"></param>
        /// <param name="Qualities"></param>
        /// <param name="TimeStamps"></param>
        private void OpcGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
        {
            if (DataChanged != null)
            {
                Dictionary<int, object> dic = new Dictionary<int, object>();
                for (int i = 1; i <= NumItems; i++)
                {
                    int handle = (int)ClientHandles.GetValue(i);
                    object value = ItemValues.GetValue(i);
                    if (dic.ContainsKey(handle))
                    {
                        dic[handle] = value;
                    }
                    else
                    {
                        dic.Add(handle, value);
                    }
                }
                if (dic.Count > 0) DataChanged?.Invoke(dic);
            }
        }

        /// <summary>
        /// 列出OPC服务器中所有节点
        /// </summary>
        /// <param name="oPCBrowser"></param>
        public void RecurBrowse(List<string> list)
        {
            OPCBrowser oPCBrowser = Server.CreateBrowser();
            //展开分支
            oPCBrowser.ShowBranches();
            //展开叶子
            oPCBrowser.ShowLeafs(true);
            foreach (object turn in oPCBrowser)
            {
                list.Add(turn.ToString());
            }
        }

        public void AddGroupItems(List<string> tags, out string error)
        {
            error = string.Empty;
            try
            {
                OpcItems = OpcGroup.OPCItems;
                for (int i = 0; i < tags.Count; i++)
                {
                    OpcItems.AddItem(tags[i], i + 1);
                }
            }
            catch (Exception err)
            {
                error = $"创建项出现错误：{err.Message}";
            }
        }

        public bool InitGroupItems(Dictionary<int, string> tags, out string error, string groupName = "Default")
        {
            error = string.Empty;
            if (tags.Count == 0)
            {
                error = "没有项要创建";
                return true;
            }
            IsItemInit = true;
            try
            {
                Server.OPCGroups.RemoveAll();
                OpcGroup = Server.OPCGroups.Add(groupName);
                OpcGroup.DataChange += OpcGroup_DataChange;
                OpcGroup.UpdateRate = 100;
                OpcGroup.IsActive = true;
                OpcGroup.IsSubscribed = true;

                OpcItems = OpcGroup.OPCItems;

                foreach (KeyValuePair<int, string> pair in tags)
                {
                    if (!string.IsNullOrEmpty(pair.Value))
                    {
                        OpcItems.AddItem(pair.Value, pair.Key);
                    }
                }

            }
            catch (Exception err)
            {
                error = $"OPC创建项出现错误：{err.Message}";
            }
            finally
            {
                //异步等待1.5秒之后将ItemInit设置为false,防止第一次加载的时候就执行OPCDa_DataChanged里面的逻辑
                Task.Factory.StartNew(() =>
                {
                    Thread.Sleep(1500);
                    IsItemInit = false;
                });
            }
            return true;
        }

        public void DisConn()
        {
            if (Server != null)
            {
                Server.Disconnect();
            }
        }

        public bool Write(string TagFullName, object value, out string error)
        {
            error = string.Empty;
            try
            {
                OpcItem = OpcItems.Item(TagFullName);
                OpcItem.Write(value);
            }
            catch (Exception err)
            {
                error = $"写入数据出现错误：Item:{TagFullName},Value:{value}，{err.Message}";
                return false;
            }
            return true;
        }

        public object ReadValue(string TagFullName, out string error)
        {
            error = string.Empty;
            object objValue = null;
            try
            {
                OpcItem = OpcItems.Item(TagFullName);
                objValue = OpcItem.Value;
            }
            catch (Exception err)
            {
                error = $"读取数据出现错误：Item:{TagFullName}，{err.Message}";
            }
            return objValue;
        }
    }
}
